package Finance::NSCC::Insurance::File::FAR;

use Moose;
extends 'Finance::NSCC::Insurance::File';
use Finance::NSCC::Insurance::Utils qw/trim/;

our $VERSION = '0.00100';

my $RHEADER = qr/^C40.{30}(.{8}).{4}.{41}(.{4}).{35}(.{10}).{35}FAR(.{12})(.).{114}$/;
my $R42   = qr/^C42(.{4})(.{4})(.{10})(.{10})(.{3}).{266}$/;
my $R4301 = qr/^C4301(.{30})(.{30})(.{4})(.{30})(.{9})(..)(.{8})(...)(.{8})(...)(.{8})(...)(.{8})(...)(.{8})(...)(.{20})(..)(.)(.{10}).{102}$/;
my $R4302 = qr/^C4302(.{30})(.)(...)(.)((.{35})(.{25})(.{25})(.{10})(.{10})).{75}(.{20})(..).{58}$/;
my $R4303 = qr/^C4303(.{30})(.{35})(.{35})(.{35})(.{30})(..)(.{15})(...).{110}$/;
my $R4304 = qr/^C4304(.{30})(.{20})(..)((.{35})(.{25})(.{25})(.{10})(.{10}))(...)(.)(.{20})(.)(.{10}).{103}$/;
my $R4305 = qr/^C4305(.{30})(.{20})(.{30})(.{16})(.)(.)(...)(.{16})(.)(...)(.{16})(.)(...)(.{16})(.)(...)(.{16})(.)(...)(.{16})(.)(...)(.{16})(.)(...)(.{16})(.)(...)(...).{17}(.{8})(.{8})(.{4}).{14}$/;
my $R4306 = qr/^C4306(.{30})(.{9})(.{5})(.{5}).(.{16})(.)(.{16})(.)(.{16})(.)(.{16})(.)(.{16})(.)(.{16})(.)(.{15}).{2}(.{18})(.)(.{9}).(.{8})(.{8})(.{8})(.{10})(.{2})(.{10})(.{2})(.{4})(.{4}).{29}.{12}$/;
my $R4307 = qr/^C4307(.{30})(.)(...)(.)((.{35})(.{25})(.{25})(.{10})(.{10})).{75}(.{20})(..).{58}$/;
my $R4308 = qr/^C4308(.{30}).{3}(.{16})(.)(.{16})(.).{4}(.{20})(.{3})(.{20})(.{3})(.{16})(.)(.{2}).{159}$/;
my $R4309 = qr/^C4309(.{30})(.{35})(.{35})(.{35})(.{30})(.{2})(.{15})(.{3}).{110}$/;

sub _process_header {
  my $self = shift;
  my $io = $self->_file_handle;
  my $head_re = ${ $self->blessed .'::RHEADER' }; #XXX
  my $line;
  if ( defined($line = $io->getline) && ( $line =~ /$head_re/) ) {
    $self->file_date( trim($1) );
    $self->nscc_num( trim($2) );
    $self->carrier_company_id( trim $3 );
    $self->record_count( $4+0 );
    $self->_test_indicator( $5 eq 'T' ? 1 : 0 );
  } else {
    $self->_error("Premature end on header record");
  }
}

sub _build_records {
  my $self = shift;
  my $io = $self->_file_handle;

  my $record_count = 1;         #header is already processed
  my @records;
  my $line = $io->getline or $self->_error("Premature end before C42 record.");
  $record_count++;
  #CONTRA record
  if ( $line !~ /$R42/ ) {
    $self->_error("'${line}' where C42 record expected.");
  }
  defined($line = $io->getline) && $record_count++;

  #CONTRACT LOOP
  while ( my(@m01) = map{ trim $_} ($line =~ /$R4301/)) {

    my $contract_num = $m01[0];
    my $contract = {
                    nscc_num     => $self->nscc_num,
                    contract_num => $self->contract_num,
                    irs_q_code   => $m01[2],
                    cusip        => $m01[4],
                   };
    push(@records, $contract);

    $contract->{group_num}     = $m01[1]  if $m01[1];
    $contract->{dist_acct_num} = $m01[3]  if $m01[3];
    $contract->{comm_opt}      = $m01[19] if $m01[19];
    if ($m01[17]) {
      $contract->{erc_id}   = $m01[16];
      $contract->{erc_id_q} = $m01[17];
    }

    #create date records from date1, date2, etc
    my @dates = map{ {date_v => $_->[0], date_q => $_->[1] } }
      map{$_->[0] =~ s/(....)(..)(..)/$1-$2-$3/; $_} grep{$_->[0] =~ /^\d{8}$/}
        ([@m01[6,7]],[@m01[8,9]],[@m01[10,11]],[@m01[12,13]],[@m01[14,15]]);
    $contract->{dates} = \@dates;

    defined($line = $io->getline) && $record_count++;

    while ( my (@m02) = map{ trim $_} ($line =~ /$R4302/)) {

      $contract->{parties} ||= []; #new arrayref if blank
      my $party = {
                   type_code => $m02[1],
                   role      => $m02[2],
                   entity_i  => $m02[3] eq 'X' ? 1 : 0,
                  };
      push(@{$contract->{parties}}, $party);

      #natural/non-natural section
      if ($party->{entity_i}) {
        $party->{entity_name} = $m02[4];
      } else {
        $party->{last_name}   = $m02[5];
        $party->{first_name}  = $m02[6] if $m02[6];
        $party->{middle_name} = $m02[7] if $m02[7];
        $party->{prefix}      = $m02[8] if $m02[8];
        $party->{suffix}      = $m02[9] if $m02[9];
      }
      if ($m02[11]) {
        $party->{tax_id}   = $m02[10];
        $party->{tax_id_q} = $m02[11];
      }
      #use if instead of while because there's only one record possible
      defined($line = $io->getline) && $record_count++;

      if ( my (@m03) = map{ trim $_} ($line =~ /$R4303/)) {

        $party->{address_line1} = $m03[1];
        $party->{address_line2} = $m03[2] if $m03[2];
        $party->{address_line3} = $m03[3] if $m03[3];
        $party->{city}          = $m03[4] if $m03[4];
        $party->{state}         = $m03[5] if $m03[5];
        $party->{zip}           = $m03[6] if $m03[6];
        $party->{country_code}  = $m03[7] if $m03[7];

      }
    }

    while ( my (@m04) = map{ trim $_} ($line =~ /$R4304/)) {

      $contract->{agents} ||= []; #new arrayref if blank
      my $agent = {
                   role      => $m04[9],
                   type_code => $m04[10],
                   entity_i  => $m04[12] eq 'X' ? 1 : 0,
                  };
      push(@{$contract->{agents}}, $agent);

      if ($m04[1]) {
        $agent->{tax_id}   = $m04[1];
        $agent->{tax_id_q} = $m04[2];
      }

      $agent->{brokers_id} = $m04[11] if $m04[11];
      $agent->{national_producer_num} = $m04[13] if $m04[13];

      #natural/non-natural section
      if ($agent->{entity_i}) {
        $agent->{entity_name} = $m04[3];
      } else {
        $agent->{last_name}   = $m04[4];
        $agent->{first_name}  = $m04[5] if $m04[5];
        $agent->{middle_name} = $m04[6] if $m04[6];
        $agent->{prefix}      = $m04[7] if $m04[7];
        $agent->{suffix}      = $m04[8] if $m04[8];
      }
      defined($line = $io->getline) && $record_count++;

    }

    while ( my (@m05) = map{ trim $_} ($line =~ /$R4305/)) {
      $contract->{transactions} ||= []; #new arrayref if blank
      my $transaction = {
                         nscc_control => $m05[1],
                         amount => ($m05[3] / 100) * ($m05[4] eq 'D' ? -1 : 1),
                         identifier => $m05[6],
                        };
      push(@{$contract->{transactions}}, $transaction);

      for ( 1 .. 7 ) {
        my $i = 7 + (($_ -1) * 3);
        if ($m05[$i+1]) {
          $transaction->{"charge_${_}"} = ($m05[$i++] / 100) * ($m05[$i++] eq 'D' ? -1 : 1);
          $transaction->{"charge_${_}_q"} = $m05[$i++];
        }
      }

      $transaction->{dist_trans_id}  = $m05[2]  if $m05[2];
      $transaction->{source_i}       = $m05[5]  if $m05[5];
      $transaction->{payment_method} = $m05[28] if $m05[28];
      $transaction->{tax_year}       = $m05[31] if $m05[31];
      if ($m05[29]) {
        $transaction->{effective_d}   = $m05[29];
        $transaction->{effective_d} =~ s/(....)(..)(..)/$1-$2-$3/;
      }
      if ($m05[30]) {
        $transaction->{process_d} = $m05[30];
        $transaction->{process_d} =~ s/(....)(..)(..)/$1-$2-$3/;
      }

      defined($line = $io->getline) && $record_count++;

      while ( my (@m06) = map{ trim $_} ($line =~ /$R4306/)) {
        $contract->{underlying_assets} ||= []; #new arrayref if blank
        my $asset = {
                     cusip       => $m06[1],
                     fund_id     => $m06[2],
                     sub_find_id => $m06[3],
                    };
        push(@{$contract->{underlying_assets}}, $asset);

        if ($m06[5] ne "") {
          $asset->{fund_amt} = ($m06[4] / 100) * ($m06[5] eq 'D' ? -1 : 1);
        }
        if ($m06[7] ne "") {
          $asset->{mva_amt} = ($m06[6] / 100) * ($m06[7] eq 'D' ? -1 : 1);
        }
        if ($m06[9] ne "") {
          $asset->{employer_amt} = ($m06[8] / 100) * ($m06[9] eq 'D' ? -1 : 1);
        }
        if ($m06[11] ne "") {
          $asset->{employee_amt} = ($m06[10] / 100) * ($m06[11] eq 'D' ? -1 : 1);
        }
        if ($m06[13] ne "") {
          $asset->{fund_surrender_chg} = ($m06[12] / 100) * ($m06[13] eq 'D' ? -1 : 1);
        }
        if ($m06[15] ne "") {
          $asset->{fund_admin_chg} = ($m06[14] / 100) * ($m06[15] eq 'D' ? -1 : 1);
        }
        if ($m06[16] ne "") {
          $asset->{fund_unit_price} = ($m06[16] / 100);
        }
        if ($m06[18] ne "") {
          $asset->{fund_units} = ($m06[17] / 100) * ($m06[18] eq 'D' ? -1 : 1);
        }

        $asset->{fund_cusip} = $m06[19] if $m06[19];
        if ($m06[20]) {
          $asset->{deposit_period_start_d} = $m06[20];
          $asset->{deposit_period_start_d} =~ s/(....)(..)(..)/$1-$2-$3/;
        }
        if ($m06[21]) {
          $asset->{deposit_period_end_d} = $m06[21];
          $asset->{deposit_period_end_d} =~ s/(....)(..)(..)/$1-$2-$3/;
        }
        if ($m06[22]) {
          $asset->{deposit_period_maturity_d} = $m06[22];
          $asset->{deposit_period_maturity_d} =~ s/(....)(..)(..)/$1-$2-$3/;
        }
        if ($m06[24]) {
          $asset->{deposit_period_rate} = ($m06[23] / (10**9));
          $asset->{deposit_period_rate_type} = $m06[24];
        }
        if ($m06[26]) {
          $asset->{deposit_period_duration} = $m06[25];
          $asset->{deposit_period_duration_q} = $m06[26];
        }
        $asset->{employer_amt_id_q} = $m06[27] if $m06[27];
        $asset->{employee_amt_id_q} = $m06[28] if $m06[28];
        defined($line = $io->getline) && $record_count++;

      }
      while ( my (@m07) = map{ trim $_} ($line =~ /$R4307/)) {
        $contract->{payee_payors} ||= []; #new arrayref if blank
        my $payee_payor = {
                           type_code => $m07[1],
                           role      => $m07[2],
                           entity_i  => $m07[3] eq 'X' ? 1 : 0,
                          };
        push(@{$contract->{payee_payors}}, $payee_payor);

        if ($m07[10]) {
          $payee_payor->{tax_id}   = $m07[10];
          $payee_payor->{tax_id_q} = $m07[11];
        }
        #natural/non-natural section
        if ($payee_payor->{entity_i}) {
          $payee_payor->{entity_name} = $m07[4];
        } else {
          $payee_payor->{last_name}   = $m07[5];
          $payee_payor->{first_name}  = $m07[6] if $m07[6];
          $payee_payor->{middle_name} = $m07[7] if $m07[7];
          $payee_payor->{prefix}      = $m07[8] if $m07[8];
          $payee_payor->{suffix}      = $m07[9] if $m07[9];
        }
        defined($line = $io->getline) && $record_count++;

        while ( my (@m08) = map{ trim $_} ($line =~ /$R4308/)) {
          $payee_payor->{net_amt} =  ($m08[1] / 100) * ($m08[2] eq 'D' ? -1 : 1);

          if ($m08[3] ne "") {
            $payee_payor->{federal_tax_whd} =  ($m08[3] / 100) * ($m08[4] eq 'D' ? -1 : 1);
          }
          if ($m08[5] ne "") {
            $payee_payor->{payor_acct_num}     = $m08[5];
            $payee_payor->{payor_acct_type_id} = $m08[6];
          }
          if ($m08[7] ne "") {
            $payee_payor->{additional_acct_num}     = $m08[7];
            $payee_payor->{additional_acct_type_id} = $m08[8];
          }
          if ($m08[9] ne "") {
            $payee_payor->{state_tax_whd} =  ($m08[9] / 100) * ($m08[10] eq 'D' ? -1 : 1);
          }
          $payee_payor->{'payment_source_amt_q'} = $m08[11];
          defined($line = $io->getline) && $record_count++;

        }
        if ( my (@m09) = map{ trim $_} ($line =~ /$R4309/)) {
          $payee_payor->{address_line1} = $m09[1];
          $payee_payor->{address_line2} = $m09[2] if $m09[2];
          $payee_payor->{address_line3} = $m09[3] if $m09[3];
          $payee_payor->{city}          = $m09[4] if $m09[4];
          $payee_payor->{state}         = $m09[5] if $m09[5];
          $payee_payor->{zip}           = $m09[6] if $m09[6];
          $payee_payor->{country_code}  = $m09[7] if $m09[7];
          defined($line = $io->getline) && $record_count++;
        }
      }
    }
  }
  my $target = $self->record_count;
  $self->_error("Record and target count mismatch ($record_count : $target)")
    unless $target == $record_count;
  $self->_clear_file_handle;    #close the filehandle
  return \@records;
}

1;
